package com.gsbelarus.gedemin.lib.sync.protocol;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;

import com.gsbelarus.gedemin.lib.R;
import com.gsbelarus.gedemin.lib.sync.protocol.interfaces.SyncProtocolModel;
import com.gsbelarus.gedemin.lib.ui.NotifHelper;

abstract public class SyncProtocolServiceModel extends Service implements SyncProtocolModel {

    protected Context context;
    protected DataBaseModel dbHelper;
    protected NotifHelper notifHelper;
    protected SyncProtocol protocol;

    public static final String STATUS_SYNC = "status_sync";
    public static final String ERROR_SYNC = "error_sync";
    public static final int DO_SYNC = 1;
    public static final int DO_SYNC_SCHEDULE = 2;
    public static final int NO_SYNC = 3;

    public static final String SUCCESS_CREATE_DEMO = "SUCCESSFUL_DEMO_CREATE";
    public static final String CLEAR_DATABASE = "clear_database";

    protected boolean isSchedule;
    public static boolean isShowNotificationError = true;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    public static String getBroadcastMessageAction(Context context) {
        return context.getPackageName() + "(broadcast_messages_about_the_status_sync)";
    }

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
        notifHelper = new NotifHelper(context, getAppName());
        dbHelper = getDataBase();
        protocol = new SyncProtocol(context, dbHelper, this);
    }

    /**
     * Для запуска сервиса вместе с синхронизацией передаем пустой intent
     * Для запуска сервиса вместе с синхронизацией по расписанию добавляем в intent SyncService.STATUS_SYNC c ключом SyncService.DO_SYNC_SCHEDULE
     * Для запуска сервиса без синхронизации добавляем в intent SyncService.STATUS_SYNC c ключом SyncService.NO_SYNC
     * Для запуска сервиса с функцией очистки бд добавляем в intent SynService.CLEAR_DATABASE с ключом true
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null)
            if (!protocol.isWork()) {
                if (intent.getBooleanExtra(CLEAR_DATABASE, false)) {
                    notifyAboutSyncStatus(DO_SYNC, null);
                    dbHelper.recreateDataBase();
                    notifyAboutSyncStatus(NO_SYNC, null);
                }
                switch (intent.getIntExtra(STATUS_SYNC, DO_SYNC)) {
                    case DO_SYNC:
                        isSchedule = false;
                        protocol.doSync();
                        break;
                    case DO_SYNC_SCHEDULE:
                        isSchedule = true;
                        protocol.doSync();
                        break;
                    case NO_SYNC:
                        stopSelf();
                        break;
                }
            } else {
                notifyAboutSyncStatus(DO_SYNC, null);
                stopSelf();
            }
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        protocol.setStopSync(true);
    }

    /**
     * Выполняется во время подготовки к созданию нового потока загрузки, имеет доступ к UI потоку
     */
    @Override
    public void onPreExecute() {
        notifHelper.clearNotification();
        protocol.setWorkMod(isWorkMode());
        if (protocol.isWorkMod())
            startForeground(NotifHelper.ID_NOTIF_FOREGROUND,
                    notifHelper.getStartSyncNotification(
                            android.R.drawable.stat_sys_download, NotifHelper.WORK));
        else
            startForeground(NotifHelper.ID_NOTIF_FOREGROUND,
                    notifHelper.getStartSyncNotification(
                            android.R.drawable.ic_popup_sync, NotifHelper.DEMO));
        notifyAboutSyncStatus(DO_SYNC, null);
    }

    /**
     * Обновление состояния загрузки
     *
     * @param percent      процент загрузки
     * @param currentBlock загружаемый блок
     * @param totalBlocks  общее количество блоков
     */
    @Override
    public void onProgressUpdate(int percent, int currentBlock, int totalBlocks) {
        if (!protocol.isStopSync())
            notifHelper.updateDownloadNotification(percent, currentBlock, totalBlocks);
    }

    /**
     * Выполняется после загрузки, имеет доступ к UI потоку
     * внутри следует обрабатывать ошибки
     *
     * @param status статус с каким завершилась синхронизация
     *               (ID_FAILED, NO_INTERNET_CONNECTION, AUTH_FAILED, ADDRESS_FAILED, TIMEOUT, NOT_REQUIRED,
     *               GUID_FAILED, PARS_FAILED, ALSE_FAILED, SUCCESSFUL, SUCCESSFUL_DEMO, STOP_SYNC)
     */
    @Override
    public void onPostExecute(SyncProtocol.SyncStatus status) {
        String statusString = null;
        switch (status) {
            case ID_FAILED:
                statusString = getString(R.string.notification_text_error_sn);
                break;
            case ADDRESS_FAILED:
                statusString = getString(R.string.notification_text_error_address);
                break;
            case AUTH_FAILED:
                statusString = getString(R.string.notification_text_error_auth);
                break;
            case TIMEOUT:
                statusString = getString(R.string.notification_text_error_timeout);
                break;
            case NO_INTERNET_CONNECTION:
                statusString = getString(R.string.notification_text_error_inernet);
                if (isSchedule) {
                    /** если по расписанию */
                    notifyAboutSyncStatus(NO_SYNC, null);
                    stopForegroundTask();
                    return;
                }
                break;
            case GUID_FAILED:
                notifyAboutSyncStatus(NO_SYNC, null);
                stopForegroundTask();
                return;
            case NOT_REQUIRED:
                notifyAboutSyncStatus(NO_SYNC, null);
                stopForegroundTask();
                return;
            case PARS_FAILED:
                statusString = getString(R.string.notification_text_error);
                break;
            case ALSE_FAILED:
                statusString = getString(R.string.notification_text_error);
                break;
            case SUCCESSFUL:
                notifyAboutSyncStatus(NO_SYNC, null);
                stopForegroundTask();
                return;
            case SUCCESSFUL_DEMO:
                statusString = getString(R.string.first_launch_text);
//                notifyAboutSyncStatus(NO_SYNC, SUCCESS_CREATE_DEMO);
//                stopForegroundTask();
//                return;
                break;
            case STOP_SYNC:
                notifyAboutSyncStatus(NO_SYNC, null);
                stopForegroundTask();
                return;
        }

        /** выводим ошибку в строку уведомлений*/
        if (isShowNotificationError)
            notifHelper.showFinishError(statusString);

        /** уведомляем активности об окончании синхронизации*/
        notifyAboutSyncStatus(NO_SYNC, statusString);
        stopForegroundTask();
    }

    private void stopForegroundTask() {
        stopForeground(true);
        notifHelper.clearForegroundNotification();
        stopSelf();
    }

    private void notifyAboutSyncStatus(int flag, String error) {
        Intent intent = new Intent(getBroadcastMessageAction(getApplicationContext()));
        intent.putExtra(STATUS_SYNC, flag);
        if (error != null)
            intent.putExtra(ERROR_SYNC, error);
        sendBroadcast(intent);
    }

    /**
     * Получение url для запроса блока
     *
     * @param ver номер запрашиваемого блока
     * @return строка url
     */
    @Override
    abstract public String getUrlStringForVer(int ver);

    /**
     * Получение url для запроса максимальной версии
     *
     * @return строка url
     */
    @Override
    abstract public String getUrlStringForMaxVer();

    /**
     * @return в каком режиме проводить синхронизацию. Вызывается перед каждой синхронизацей.
     */
    abstract protected boolean isWorkMode();

    /**
     * @return объект для работы с базой данных
     */
    abstract protected DataBaseModel getDataBase();

    /**
     * @return локализованное название приложения, используется в шторке уведомлений
     */
    abstract protected String getAppName();
}
